// main.go
package main

import (
	"context"                                                 // 上下文管理（用于追踪链路传递）
	"github.com/gin-gonic/gin"                                // Web框架
	"github.com/prometheus/client_golang/prometheus"          // Prometheus核心库
	"github.com/prometheus/client_golang/prometheus/promhttp" // 提供Prometheus HTTP Handler
	"go.opentelemetry.io/otel"                                // OpenTelemetry核心API
	"go.opentelemetry.io/otel/exporters/jaeger"               // Jaeger导出器（将追踪数据发送到Jaeger）
	"go.opentelemetry.io/otel/sdk/resource"                   // 资源管理（标识服务信息）
	sdktrace "go.opentelemetry.io/otel/sdk/trace"             // 追踪SDK实现
	semconv "go.opentelemetry.io/otel/semconv/v1.21.0"        // 语义约定（标准化标签名）
	"log"
	"math/rand"
	"net/http"
	"time"
)

/******************* Prometheus指标定义 *******************/
var (
	// httpRequests 定义计数器指标（记录总请求数）
	// Name: 指标名称
	// Help: 帮助说明
	// Labels: 维度标签（方法、端点、状态码）
	httpRequests = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "http_requests_total",
			Help: "Total number of HTTP requests",
		},
		[]string{"method", "endpoint", "status"},
	)

	// httpDuration 定义直方图指标（记录请求耗时分布）
	// Buckets: 定义统计区间（0.1s,0.3s,0.5s,1s）
	httpDuration = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "http_request_duration_seconds",
			Help:    "Duration of HTTP requests",
			Buckets: []float64{0.1, 0.3, 0.5, 1.0},
		},
		[]string{"method", "endpoint"},
	)
)

// init 函数会在main执行前自动调用，用于注册指标
func init() {
	// 必须注册指标才能被采集
	prometheus.MustRegister(httpRequests, httpDuration)
}

/******************* OpenTelemetry追踪初始化 *******************/

// initTracer 初始化Jaeger追踪器
func initTracer() *sdktrace.TracerProvider {
	// 1. 创建Jaeger导出器（指向Jaeger Collector地址）
	exp, err := jaeger.New(jaeger.WithCollectorEndpoint(
		jaeger.WithEndpoint("http://jaeger:14268/api/traces"),
	))
	if err != nil {
		log.Fatal("无法创建Jaeger导出器:", err)
	}

	// 2. 配置追踪资源（标识当前服务）
	res := resource.NewWithAttributes(
		semconv.SchemaURL,                                      // 使用语义约定规范
		semconv.ServiceNameKey.String("demo-app"),              // 服务名称
		semconv.DeploymentEnvironmentKey.String("development"), // 环境标识
	)

	// 3. 创建追踪提供器（TracerProvider）
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exp),                     // 批量发送数据到jaeger导出器
		sdktrace.WithResource(res),                    // 关联资源信息
		sdktrace.WithSampler(sdktrace.AlwaysSample()), // 采样率设置（此处100%采样）
	)

	// 4. 设置为全局提供器,后续可通过 otel.Tracer()获取
	otel.SetTracerProvider(tp)
	return tp
}

/******************* 主函数入口 *******************/
func main() {
	// 初始化追踪并设置清理函数
	tp := initTracer()
	defer func() {
		if err := tp.Shutdown(context.Background()); err != nil {
			log.Fatal("追踪器关闭失败:", err)
		}
	}()

	// 创建Gin路由引擎
	r := gin.Default()

	// 暴露Prometheus指标端点（重要：需与prometheus.yml配置对应）
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))

	// 定义业务接口 /hello
	r.GET("/hello", func(c *gin.Context) {
		startTime := time.Now() // 记录开始时间

		// 模拟业务处理耗时（0-500ms随机）
		time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)

		// 记录Prometheus指标（必须与定义时的标签顺序一致）
		// Inc() 增加计数器
		httpRequests.WithLabelValues(
			"GET",    // method标签值
			"/hello", // endpoint标签值
			"200",    // status标签值（假设总是成功）
		).Inc()

		// 记录耗时到直方图（自动分配到预设的Bucket）
		// Observe() 传入耗时秒数
		httpDuration.WithLabelValues("GET", "/hello").Observe(
			time.Since(startTime).Seconds(),
		)

		// 返回JSON响应
		c.JSON(http.StatusOK, gin.H{"message": "keep going !!!"})
	})

	// 启动HTTP服务（端口8080）
	if err := r.Run(":8080"); err != nil {
		log.Fatal("服务器启动失败:", err)
	}
}
